/**
 * @Author: YangYun
 * @Date: 2020/7/24 9:43
 */
package lib

import (
	"encoding/json"
	"errors"
	"git.900sui.cn/kc/kcgin"
	"github.com/tidwall/gjson"
	"io/ioutil"
	"net/http"
	"strings"
	"time"
)

const (
	CCB_FUND_suc  = "0"
	CCB_FUND_fild = "1"
)

type ccbCustody struct {
	url       string
	updateurl string
}

var (
	CcbCustody *ccbCustody
)

type resultInterface interface {
	getCode() string
	getDesc() string
}

type result struct {
	RespCode string `json:"respCode"`
	RespDesc string `json:"respDesc"`
}

func (r *result) getCode() string {
	return r.RespCode
}

func (r *result) getDesc() string {
	return r.RespDesc
}

type upResult struct {
	Error    int         `json:"error"`
	ErrorMsg string      `json:"errorMsg"`
	Data     interface{} `json:"data"`
}

func init() {
	CcbCustody = &ccbCustody{
		url:       kcgin.AppConfig.String("ccbpay.custody.url"),
		updateurl: kcgin.AppConfig.String("ccbpay.custody.upurl"),
	}
}

// 子账户商户签约
// @param seqno       // 流水号
// @param companyCode // 商户id
// @param companyName // 商户名称
// @param bankAccount // 银行账号
// @param bankAccountName // 银行账户名称
// @param bankName    // 开户行
// @param bankCode    // 开户行联行号
// @param contactName // 联系人姓名
// @param contactTel  // 联系人手机号
func (c *ccbCustody) Signed(seqno, companyCode, companyName, bankAccount, bankAccountName, bankName, bankCode, contactName, contactTel string) (*gjson.Result, error) {
	data := map[string]interface{}{
		"seqno":           seqno,
		"timestamp":       time.Now().Format("20060102150405"),
		"funcode":         "1",
		"companycode":     companyCode,
		"companyname":     companyName,
		"bankaccount":     bankAccount,
		"bankaccountname": bankAccountName,
		"bankname":        bankName,
		"bankcode":        bankCode,
		"contactname":     contactName,
		"contacttel":      contactTel,
	}
	return nil, c.exec("01", data, nil)
}

// 子账户商户签约信息修改
// @param seqno       // 流水号
// @param companyCode // 商户id
// @param companyName // 商户名称
// @param bankAccount // 银行账号
// @param bankAccountName // 银行账户名称
// @param bankName    // 开户行
// @param bankCode    // 开户行联行号
// @param contactName // 联系人姓名
// @param contactTel  // 联系人手机号
func (c *ccbCustody) SignedEdit(seqno, companyCode, companyName, bankAccount, bankAccountName, bankName, bankCode, contactName, contactTel string) (*gjson.Result, error) {
	data := map[string]interface{}{
		"seqno":           seqno,
		"timestamp":       time.Now().Format("20060102150405"),
		"funcode":         "3",
		"companycode":     companyCode,
		"companyname":     companyName,
		"bankaccount":     bankAccount,
		"bankaccountname": bankAccountName,
		"bankname":        bankName,
		"bankcode":        bankCode,
		"contactname":     contactName,
		"contacttel":      contactTel,
	}
	return nil, c.exec("01", data, nil)
}

// 子账户商户信息查询
// @param seqno       // 流水号
// @param companyCode // 商户id
// @param companyName // 商户名称
func (c *ccbCustody) SignedQuery(seqno, companyCode, companyName string) (*gjson.Result, error) {
	data := map[string]interface{}{
		"seqno":       seqno,
		"timestamp":   time.Now().Format("20060102150405"),
		"funcode":     "4",
		"companycode": companyCode,
		"companyname": companyName,
	}
	return nil, c.exec("01", data, nil)
}

type upfile struct {
	Name string `json:"name"`
	Path string `json:"path"`
}

// 清分文件上传
// @param fileName // 上传文件名称
// @param content  // 上传文件内容
func (c *ccbCustody) FundUpload(fileName, content string) (*upfile, error) {
	result, err := c.request("post", c.updateurl+fileName, content)
	if err != nil {
		return nil, err
	}
	res := upResult{Data: &upfile{}}
	if err := json.Unmarshal([]byte(result), &res); err != nil {
		return nil, err
	}
	if res.Error == 0 {
		return res.Data.(*upfile), nil
	}
	return nil, errors.New(res.ErrorMsg)
}

type fund struct {
	result
	Batno int `json:"batno"`
}

// 资金清分
// @param seqno    // 流水号
// @param path     // 文件路径
// @param fileName // 文件名称
// @param sum      // 总金额
// @param count    // 条目数
func (c *ccbCustody) Fund(seqno, path, fileName, sum string, count int) (resFund fund, err error) {
	data := map[string]interface{}{
		"seqno":     seqno,
		"timestamp": time.Now().Format("20060102150405"),
		"url":       path,
		"filename":  fileName,
		"sum":       sum,
		"count":     count,
	}
	err = c.exec("02", data, &resFund)
	return
}

type fundResult struct {
	result
	FileContent string `json:"filecontent"`
}

// 清分结果
// @param seqno     // 流水号
// @param queryDate // 清分日期 格式 Ymd
// @param batNo     // 批次号
func (c *ccbCustody) FundResult(seqno, queryDate, batNo string) (resFundResult fundResult, err error) {
	data := map[string]interface{}{
		"seqno":     seqno,
		"timestamp": time.Now().Format("20060102150405"),
		"filetype":  "1",
		"querydate": queryDate,
		"batno":     batNo,
	}
	err = c.exec("07", data, &resFundResult)
	return
}

// 资金支取
// @param seqno          // 流水号
// @param subAccount     // 子账户账号
// @param subAccountName // 子账户户名
// @param companyCode    // 子账户商户编号
// @param bankAccount    // 结算账户账号
// @param bankAccountName // 结算账户户名
// @param bankName       // 结算账户开户行
// @param bankCode       // 结算账户联行号
// @param amount         // 支取金额
func (c *ccbCustody) Cash(seqno, subAccount, subAccountName, companyCode, bankAccount, bankAccountName, bankName, bankCode, amount string) (*gjson.Result, error) {
	data := map[string]interface{}{
		"seqno":           seqno,
		"timestamp":       time.Now().Format("20060102150405"),
		"bankaccount":     subAccount,
		"bankaccountname": subAccountName,
		"companycode":     companyCode,
		"rcvaccount":      bankAccount,
		"rcvacctountname": bankAccountName,
		"rcvbankname":     bankName,
		"rcvbankcode":     bankCode,
		"riskfund":        amount,
	}
	return nil, c.exec("03", data, nil)
}

// 资金支取查询
// @param seqno          // 流水号
// @param oldSeqno       // 支取流水号
// @param companyCode    // 子账户商户编号
func (c *ccbCustody) CashQuery(seqno, oldSeqno, companyCode string) (*gjson.Result, error) {
	data := map[string]interface{}{
		"seqno":       seqno,
		"timestamp":   time.Now().Format("20060102150405"),
		"old_seqno":   oldSeqno,
		"companycode": companyCode,
	}
	return nil, c.exec("04", data, nil)
}

// 资金支取结果
// @param seqno          // 流水号
// @param queryDate      // 查询日期 格式 Ymd
func (c *ccbCustody) CashResult(seqno, queryDate string) (*gjson.Result, error) {
	data := map[string]interface{}{
		"seqno":     seqno,
		"timestamp": time.Now().Format("20060102150405"),
		"filetype":  "2",
		"querydate": queryDate,
	}
	return nil, c.exec("07", data, nil)
}

// 资金支取结果
// @param seqno          // 流水号
// @param account        // 账号
// @param accountName    // 户名
func (c *ccbCustody) Blanch(seqno, account, accountName string) (*gjson.Result, error) {
	data := map[string]interface{}{
		"seqno":           seqno,
		"timestamp":       time.Now().Format("20060102150405"),
		"bankaccount":     account,
		"bankaccountname": accountName,
	}
	return nil, c.exec("05", data, nil)
}

// 账户明细
// @param seqno          // 流水号
// @param account        // 账号
// @param startDate      // 户名
// @param endDate        // 户名
func (c *ccbCustody) Info(seqno, acount, startDate, endDate string) (*gjson.Result, error) {
	data := map[string]interface{}{
		"seqno":       seqno,
		"timestamp":   time.Now().Format("20060102150405"),
		"bankaccount": acount,
		"startdate":   startDate,
		"enddate":     endDate,
	}
	return nil, c.exec("06", data, nil)
}

// 执行
func (c *ccbCustody) exec(action string, data map[string]interface{}, res resultInterface) error {
	marshal, err := json.Marshal(data)
	if err != nil {
		return err
	}
	result, err := c.request("post", c.url+action, string(marshal), &map[string]string{"Content-Type": "application/x-www-form-urlencoded"})
	if err := json.Unmarshal([]byte(result), res); err != nil {
		return err
	}

	if res.getCode() == "0000" {
		return nil
	}
	return errors.New(res.getDesc())
}

// 网络请求
func (c *ccbCustody) request(method string, host string, data string, headers ...*map[string]string) (string, error) {
	client := &http.Client{}
	method = strings.ToUpper(method)

	req, err := http.NewRequest(method, host, strings.NewReader(data))
	if err != nil {
		return "", err
	}
	if len(headers) > 0 {
		for key, value := range *headers[0] {
			req.Header.Add(key, value)
		}
	}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(body), nil
}
